
$RedAnim_GStatePersistence = 5; // G state must stay the same for this many 33-ms ticks before being updated
$RedAnim_SoundThreadSpeed = 0;
$RedAnim_AnimThreadSpeed = 0;
$RedAnim_SoundThreadG = 1;
$RedAnim_AnimThreadG = 1;

if(!isFunction(WheeledVehicleData, onAdd  )) eval("function WheeledVehicleData::onAdd  (){}");
if(!isFunction(FlyingVehicleData , onAdd  )) eval("function FlyingVehicleData ::onAdd  (){}");
if(!isFunction(Armor             , onMount)) eval("function Armor             ::onMount(){}");

package RedBase_Animations {
	function WheeledVehicleData::onAdd(%data, %vehicle) {
		parent::onAdd(%data, %vehicle);
		RedAnim_VehicleCreated(%vehicle);
	}
	function FlyingVehicleData::onAdd(%data, %vehicle) {
		parent::onAdd(%data, %vehicle);
		RedAnim_VehicleCreated(%vehicle);
	}
	function Armor::onMount(%data, %player, %obj, %slot) {
		parent::onMount(%data, %player, %obj, %slot);
		RedAnim_VehicleUpdate(%obj);
	}
	function Armor::doDismount(%data, %player, %forced) {
		%vehicle = %player.getObjectMount();
		parent::doDismount(%data, %player, %forced);
		RedAnim_VehicleUpdate(%vehicle);
	}
	
};
activatePackage(RedBase_Animations);

function RedAnim_VehicleCreated(%vehicle) {
	%data = %vehicle.getDatablock();
	
	// Custom wheels and springs
	for(%i=0; %i<%data.numWheels; %i++) {
		if(%data.wheelTire    [%i] !$= "") { %vehicle.setWheelTire    (%i, %data.wheelTire    [%i]); }
		if(%data.wheelSpring  [%i] !$= "") { %vehicle.setWheelSpring  (%i, %data.wheelSpring  [%i]); }
		if(%data.wheelSteering[%i] !$= "") { %vehicle.setWheelSteering(%i, %data.wheelSteering[%i]); }
	}
	
	RedAnim_VehicleUpdate(%vehicle);
}

function RedAnim_VehicleUpdate(%vehicle) {
	if(!isObject(%vehicle)) return;
	cancel(%vehicle.RedAnim_UpdateSched);
	
	%speedState = RedAnim_UpdateSpeedState(%vehicle);
	RedAnim_UpdateGForce(%vehicle);
	
	%delay = %speedState==0 ? 1000 : 33;
	%vehicle.RedAnim_UpdateSched = schedule(%delay, 0, RedAnim_VehicleUpdate, %vehicle);
}

function RedAnim_UpdateGForce(%vehicle) {
	%data = %vehicle.getDatablock();
	if(%data.gLimit $= "") return;
	
	%time = getSimTime();
	%vel = %vehicle.getVelocity();
	%delta = %time - %vehicle.RedAnim_LastTime;
	%lastgstate = %vehicle.RedAnim_LastGState;
	if(%delta < 100) {
		%accel = vectorLen(vectorSub(%vel, %vehicle.RedAnim_LastVel)) * (%delta/1000);
		%gstate = %accel > %data.gLimit;
		if(%gstate == %lastgstate) {
			%vehicle.RedAnim_GStateChangeCounter = 0;
		} else {
			%vehicle.RedAnim_GStateChangeCounter++;
			if(%vehicle.RedAnim_GStateChangeCounter >= $RedAnim_GStatePersistence) { // only apply if state stays different for 5 consecutive ticks
				RedAnim_AnimateGStateTransition(%data, %vehicle, %gstate);
				if(%gstate) { RedAnim_Callback(%data, onLowG , %vehicle); }
				else        { RedAnim_Callback(%data, onHighG, %vehicle); }
				%vehicle.RedAnim_LastGState = %gstate;
			}
		}
	}
	%vehicle.RedAnim_LastTime = %time;
	%vehicle.RedAnim_LastVel = %vel;
}

function RedAnim_GetSpeedState(%vehicle) {
	%data = %vehicle.getDatablock();
	
	%speed = vectorLen(%vehicle.getVelocity());
	if     (%speed < %data.slowSpeed     || (%data.slowSpeed $= "" && %speed < 2)) %speedState = 1;
	else if(%speed < %data.fastSpeed     || %data.fastSpeed     $= "")             %speedState = 2;
	else if(%speed < %data.veryFastSpeed || %data.veryFastSpeed $= "")             %speedState = 3;
	else                                                                           %speedState = 4;
	
	%numRiding = 0;
	for(%i=0; %i<%vehicle.getMountedObjectCount(); %i++) {
		%obj = %vehicle.getMountedObject(%i);
		if(%obj.getClassName() $= "Player") {
			%numRiding++;
		}
	}
	if(%speedState == 1 && %numRiding == 0) %speedState = 0;
	
	return %speedState;
}

function RedAnim_UpdateSpeedState(%vehicle) {
	%speedState = RedAnim_GetSpeedState(%vehicle);
	
	%prevState = %vehicle.RedAnim_SpeedState;
	%vehicle.RedAnim_SpeedState = %speedState;
	if(%speedState != %prevState) {
		if(%prevState < %speedState) {
			for(%i = %prevState+1; %i <= %speedState; %i++) {
				RedAnim_SetSpeedState(%vehicle, %i, 1);
			}
		} else {
			for(%i = %prevState-1; %i >= %speedState; %i--) {
				RedAnim_SetSpeedState(%vehicle, %i, -1);
			}
		}
	} else if(%prevState $= "") {
		RedAnim_SetSpeedState(%vehicle, 0, 1);
	}
	
	return %speedState;
}

//function WheeledVehicleData::onOff     (%data, %vehicle, %dir) { if(isFunction(%data, onOff     )) %data.onOff     (%vehicle, %dir); }
//function WheeledVehicleData::onIdle    (%data, %vehicle, %dir) { if(isFunction(%data, onIdle    )) %data.onIdle    (%vehicle, %dir); }
//function WheeledVehicleData::onSlow    (%data, %vehicle, %dir) { if(isFunction(%data, onSlow    )) %data.onSlow    (%vehicle, %dir); }
//function WheeledVehicleData::onFast    (%data, %vehicle, %dir) { if(isFunction(%data, onFast    )) %data.onFast    (%vehicle, %dir); }
//function WheeledVehicleData::onVeryFast(%data, %vehicle, %dir) { if(isFunction(%data, onVeryFast)) %data.onVeryFast(%vehicle, %dir); }
//function WheeledVehicleData::onLowG    (%data, %vehicle      ) { if(isFunction(%data, onLowG    )) %data.onLowG    (%vehicle      ); }
//function WheeledVehicleData::onHighG   (%data, %vehicle      ) { if(isFunction(%data, onHighG   )) %data.onHighG   (%vehicle      ); }
//function FlyingVehicleData ::onOff     (%data, %vehicle, %dir) { if(isFunction(%data, onOff     )) %data.onOff     (%vehicle, %dir); }
//function FlyingVehicleData ::onIdle    (%data, %vehicle, %dir) { if(isFunction(%data, onIdle    )) %data.onIdle    (%vehicle, %dir); }
//function FlyingVehicleData ::onSlow    (%data, %vehicle, %dir) { if(isFunction(%data, onSlow    )) %data.onSlow    (%vehicle, %dir); }
//function FlyingVehicleData ::onFast    (%data, %vehicle, %dir) { if(isFunction(%data, onFast    )) %data.onFast    (%vehicle, %dir); }
//function FlyingVehicleData ::onVeryFast(%data, %vehicle, %dir) { if(isFunction(%data, onVeryFast)) %data.onVeryFast(%vehicle, %dir); }
//function FlyingVehicleData ::onLowG    (%data, %vehicle      ) { if(isFunction(%data, onLowG    )) %data.onLowG    (%vehicle      ); }
//function FlyingVehicleData ::onHighG   (%data, %vehicle      ) { if(isFunction(%data, onHighG   )) %data.onHighG   (%vehicle      ); }

function RedAnim_Callback(%data, %func, %vehicle, %dir) {
	if(isFunction(%data.getName(), %func)) %data.call(%func, %vehicle, %dir);
}

$RedAnim_SpeedStateNames[0] = "Off";
$RedAnim_SpeedStateNames[1] = "Idle";
$RedAnim_SpeedStateNames[2] = "Slow";
$RedAnim_SpeedStateNames[3] = "Fast";
$RedAnim_SpeedStateNames[4] = "VeryFast";
function RedAnim_SetSpeedState(%vehicle, %speedState, %dir) {
	%data = %vehicle.getDatablock();
	if     (%speedState==0) { RedAnim_Callback(%data, onOff     , %vehicle, %dir); }
	else if(%speedState==1) { RedAnim_Callback(%data, onIdle    , %vehicle, %dir); }
	else if(%speedState==2) { RedAnim_Callback(%data, onSlow    , %vehicle, %dir); }
	else if(%speedState==3) { RedAnim_Callback(%data, onFast    , %vehicle, %dir); }
	else if(%speedState==4) { RedAnim_Callback(%data, onVeryFast, %vehicle, %dir); }
	RedAnim_AnimateSpeedStateTransition(%data, %vehicle, %speedState, %dir);
}

// animation updates

function RedAnim_PlaySound(%vehicle, %startfx, %startfxtime, %contfx, %thread) {
	if(%startfx !$= "") {
		%vehicle.playAudio(%thread, %startfx);
		if(%contfx !$= "") %vehicle.RedAnim_AnimationSchedule = %vehicle.schedule(%startfxtime, playAudio, %thread, %contfx);
	} else {
		if(%contfx !$= "") %vehicle.playAudio(%thread, %contfx);
		else               %vehicle.stopAudio(%thread);
	}
}

function RedAnim_PlayAnimation(%vehicle, %startfx, %startfxtime, %contfx, %thread) {
	if(%startfx !$= "") {
		%vehicle.playThread(%thread, %startfx);
		if(%contfx !$= "") %vehicle.RedAnim_AnimationSchedule = %vehicle.schedule(%startfxtime, playThread, %thread, %contfx);
	} else {
		if(%contfx !$= "") %vehicle.playThread(%thread, %contfx);
		else               %vehicle.playThread(%thread, root);
	}
}

function RedAnim_AnimateSpeedStateTransition(%data, %vehicle, %speedState, %dir) {
	%i = $RedAnim_SpeedStateNames[%speedState];
	
	cancel(%vehicle.RedAnim_SoundSchedule    );
	cancel(%vehicle.RedAnim_AnimationSchedule);
	if(%dir==1) {
		RedAnim_PlaySound    (%vehicle, %data.stateUpSound      [%i], %data.stateUpSoundTimeMs      [%i], %data.stateSound    [%i], $RedAnim_SoundThreadSpeed);
		RedAnim_PlayAnimation(%vehicle, %data.stateUpAnimation  [%i], %data.stateUpAnimationTimeMs  [%i], %data.stateAnimation[%i], $RedAnim_AnimThreadSpeed );
	} else {
		RedAnim_PlaySound    (%vehicle, %data.stateDownSound    [%i], %data.stateDownSoundTimeMs    [%i], %data.stateSound    [%i], $RedAnim_SoundThreadSpeed);
		RedAnim_PlayAnimation(%vehicle, %data.stateDownAnimation[%i], %data.stateDownAnimationTimeMs[%i], %data.stateAnimation[%i], $RedAnim_AnimThreadSpeed );
	}
	
	%image = %data.stateImage[%i];
	%slot = %data.stateImageSlot;
	if(%image !$= "" && %image.getID() != %vehicle.RedAnim_StateImageMounted) {
		if(%vehicle.RedAnim_StateImageMounted) %vehicle.unmountImage(%slot);
		%vehicle.mountImage(%image, %slot);
		%vehicle.RedAnim_StateImageMounted = %image.getID();
	} else if(%image $= "") {
		if(%vehicle.RedAnim_StateImageMounted) %vehicle.unmountImage(%slot);
	}
}

function RedAnim_AnimateGStateTransition(%data, %vehicle, %gstate) {
	cancel(%vehicle.RedAnim_GSoundSchedule);
	cancel(%vehicle.RedAnim_GAnimationSchedule);
	if(%gstate) {
		RedAnim_PlaySound    (%vehicle, %data.highGStartSound    , %data.highGStartSoundTimeMs    , %data.highGSound    , $RedAnim_SoundThreadG);
		RedAnim_PlayAnimation(%vehicle, %data.highGStartAnimation, %data.highGStartAnimationTimeMs, %data.highGAnimation, $RedAnim_AnimThreadG );
	} else {
		RedAnim_PlaySound    (%vehicle, %data.highGStopSound     , %data.highGStopSoundTimeMs     , ""                  , $RedAnim_SoundThreadG);
		RedAnim_PlayAnimation(%vehicle, %data.highGStopAnimation , %data.highGStopAnimationTimeMs , ""                  , $RedAnim_AnimThreadG );
	}
}
